#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../fileSystem/matrix.h"
#include "../fileSystem/file.h"
#include "motorConsultas.h"
#include "Lista.h"

#define TYPE_ACTOR 0
#define TYPE_DIRECTOR 1
#define TYPE_WRITER 2

#define LONGMIN -1
#define NO_HAY_CAMINO -2

typedef struct {
	int dupla[2];
	unsigned peli;
}parActores;

int getRelacion(TRelacion *rel, unsigned long idPer, int idPel, fHandler *fPer, fHandler *fPeli);
void invertirLista(TLista *ls);
void construirSalida(TLista *ls, TLista lsPares, fHandler *fPer, fHandler *fPeli, int idPer1, int idPer2);
void ordenarCamino(TLista *ls, int idP1, int idP2);

int consultarPorPersona(TLista *ls, mHandler *matrix, fHandler* films, fHandler* person, unsigned idPer1, unsigned idPer2) {
	if (idPer1 == idPer2) return -1;
	if (!matrix) return -1;
	if (!films) return -1;
	if (!person) return -1;

	regMat reg, aux1, aux2;
	TRelacion relacion;
	parActores par;
	TLista lsPares;
	unsigned i, j;
	int detener = 0;

	lista_Crear(ls, sizeof(TRelacion));
	lista_Crear(&lsPares, sizeof(parActores));

	if ( idPer1 < idPer2 ) {
		i = idPer1;
		j = idPer2;
	}else {
		i = idPer2;
		j = idPer1;
	}

	aux1.idPer = 0;
	aux2.idPer = 0;

	M_READ(matrix, i, j, &reg);

	if ( reg.idPer == NO_HAY_CAMINO )
		return NO_HAY_CAMINO;

	if (reg.idPer == LONGMIN) {
		getRelacion(&relacion, idPer1, reg.idPel, person, films);
		lista_Insertar(ls, LSC_PRIMERO, &relacion);

		getRelacion(&relacion, idPer2, -1, person, films);
		lista_Insertar(ls, LSC_SIGUIENTE, &relacion);

		return 0;
	}

	M_READ(matrix, i, reg.idPer, &aux1);
	M_READ(matrix, j, reg.idPer, &aux2);

	if ( (aux1.idPer == NO_HAY_CAMINO) || (aux2.idPer == NO_HAY_CAMINO) )
		return NO_HAY_CAMINO;

	if ( (aux1.idPer == LONGMIN) && (aux2.idPer == LONGMIN) ) {
		getRelacion(&relacion, i, aux1.idPel, person, films);
		lista_Insertar(ls, LSC_PRIMERO, &relacion);

		getRelacion(&relacion, reg.idPer, aux2.idPel, person, films);
		lista_Insertar(ls, LSC_SIGUIENTE, &relacion);

		getRelacion(&relacion, j, -1, person, films);
		lista_Insertar(ls, LSC_SIGUIENTE, &relacion);

		if ( idPer1 > idPer2) {
			invertirLista(ls);
		}

		return 0;
	}

	while( !detener ) {
		M_READ(matrix, reg.idPer, i, &aux1);
		M_READ(matrix, reg.idPer, j, &aux2);

		if ( (aux1.idPer == NO_HAY_CAMINO) || (aux2.idPer == NO_HAY_CAMINO) )
			return NO_HAY_CAMINO;

		if ( (aux1.idPer != LONGMIN) && (aux2.idPer == LONGMIN)) {
			par.dupla[0] = reg.idPer;
			par.dupla[1] = j;
			par.peli = aux2.idPel;
			if (lista_Vacia(lsPares))
				lista_Insertar(&lsPares, LSC_PRIMERO, &par);
			else
				lista_Insertar(&lsPares, LSC_SIGUIENTE, &par);
			j = reg.idPer;
			reg.idPer = aux1.idPer;
		}

		if ( (aux1.idPer == LONGMIN) && (aux2.idPer != LONGMIN)) {
			par.dupla[0] = reg.idPer;
			par.dupla[1] = i;
			par.peli = aux1.idPel;
			if (lista_Vacia(lsPares))
				lista_Insertar(&lsPares, LSC_PRIMERO, &par);
			else
				lista_Insertar(&lsPares, LSC_SIGUIENTE, &par);
			i = reg.idPer;
			reg.idPer = aux2.idPer;
		}

		if ( (aux1.idPer == LONGMIN) && (aux2.idPer == LONGMIN))
			detener = 1;
	}

	par.dupla[0] = reg.idPer;
	par.dupla[1] = i;
	par.peli = aux1.idPel;
	lista_Insertar(&lsPares, LSC_SIGUIENTE, &par);
	par.dupla[0] = reg.idPer;
	par.dupla[1] = j;
	par.peli = aux2.idPel;
	lista_Insertar(&lsPares, LSC_SIGUIENTE, &par);

	ordenarCamino(&lsPares, idPer1, idPer2);

	construirSalida(ls, lsPares, person, films, idPer1, idPer2);

	lista_Vaciar(&lsPares);

	return 0;
}

int consultarPorGrados(mHandler *matrix, fHandler* films, fHandler* person, unsigned idPer, char grado) {
	if (grado < 1) return -1;
	if (idPer < 0) return -1;
	if (!matrix) return -1;
	if (!person) return -1;
	if (!films) return -1;

	regMat aux;

	unsigned i;
	char input ='a';
	unsigned validar = 0;
	unsigned contador = 0;
	TRelacion rel;

	for (i = 0 ; i < idPer ; i++) {
		M_READ(matrix, idPer, i, &aux);
		if (aux.dist == grado) {
			getRelacion(&rel, i, -1, person, films);
			printf("%s (%s)\n",rel.nombre, rel.profesion);
			contador++;
			validar = 1;
			if (contador == 10){
				printf("\nIngresar 'Enter' para continuar o 'f'/'F' para salir... ");
				fflush(stdin);
				input = getchar();
				contador = 0;
				if ( (input == 'f') || (input == 'F') )
					return 0;
			}
		}
	}

	i = idPer + 1;
	while(M_READ(matrix, i, idPer, &aux) != RES_ERROR) {
		if (aux.dist == grado) {
			getRelacion(&rel, i, -1, person, films);
			printf("%s (%s)\n", rel.nombre, rel.profesion);
			contador++;
			validar = 1;
			if (contador == 10){
				printf("\nIngresar 'Enter' para continuar o 'f'/'F' para salir... ");
				fflush(stdin);
				input = getchar();
				contador = 0;
				if ( (input == 'f') || (input == 'F') )
					return 0;
			}
		}
		i++;
	}

	if (!validar)
		printf("\nNo existen personas a grado de separacion %i.\n\n",grado);

	return 0;
}

/**
 *	Obtiene el nombre de una persona
 */
int getRelacion(TRelacion *rel, unsigned long idPer, int idPel, fHandler *fPer, fHandler *fPeli) {
	regPer personReg;
	regPel peliReg;
	R_READ(fPer, idPer, &personReg);

	strcpy(rel->nombre, personReg.nombre);

	switch (personReg.rubro) {
		case TYPE_ACTOR : strcpy(rel->profesion, "actor"); break;
		case TYPE_DIRECTOR : strcpy(rel->profesion, "director"); break;
		case TYPE_WRITER : strcpy(rel->profesion, "escritor"); break;
	}

	if (idPel >= 0) {
		R_READ(fPeli, idPel, &peliReg);
		strcpy(rel->pelicula, peliReg.nombre);
		return 0;
	}

	return 0;
}

void invertirLista(TLista *ls) {
	TLista aux;
	TRelacion rel, relAux;

	lista_Crear(&aux, sizeof(TRelacion));

	while ( lista_MoverCorriente(ls, LSC_SIGUIENTE) ){

	}

	lista_ElemCorriente(*ls, &rel);
	lista_BorrarCorriente(ls);
	if (!lista_Vacia(*ls))
		lista_ElemCorriente(*ls, &relAux);
	strcpy(rel.pelicula, relAux.pelicula);
	lista_Insertar(&aux, LSC_PRIMERO, &rel);

	while ( !lista_Vacia(*ls) ) {
		lista_ElemCorriente(*ls, &rel);
		lista_BorrarCorriente(ls);
		if (!lista_Vacia(*ls))
			lista_ElemCorriente(*ls, &relAux);
		strcpy(rel.pelicula, relAux.pelicula);
		lista_Insertar(&aux, LSC_SIGUIENTE, &rel);
	}
	lista_Vaciar(ls);
	*ls = aux;
}

void ordenarCamino(TLista *ls, int idP1, int idP2) {
	parActores aux;
	TLista lsAux;
	int idBuscar, OK;
	int validar = 0;

	lista_Crear(&lsAux, sizeof(parActores));

	idBuscar = idP1;

	while ( idBuscar != idP2 ) {
		lista_MoverCorriente(ls, LSC_PRIMERO);
		OK = 0;
		validar = 0;

		while ( !validar ) {
			lista_ElemCorriente(*ls, &aux);
			if ( (aux.dupla[0] == idBuscar) || (aux.dupla[1] == idBuscar) ) {
				if (lista_Vacia(lsAux))
					lista_Insertar(&lsAux, LSC_PRIMERO, &aux);
				else
					lista_Insertar(&lsAux, LSC_SIGUIENTE, &aux);
				lista_BorrarCorriente(ls);

				if ( aux.dupla[0] != idBuscar ) {
					idBuscar = aux.dupla[0];
					OK = 1;
				}else {
					idBuscar = aux.dupla[1];
					OK = 1;
				}
			}
			lista_MoverCorriente(ls, LSC_SIGUIENTE);
			if ( OK || lista_Vacia(*ls) )
				validar = 1;
		}
	}

	lista_Vaciar(ls);
	*ls = lsAux;
}

void construirSalida(TLista *ls, TLista lsPares, fHandler *fPer, fHandler *fPeli, int idPer1, int idPer2) {

	parActores aux;
	TRelacion rel;
	int err = 1;
	int idAux = idPer1;

	lista_MoverCorriente(&lsPares, LSC_PRIMERO);

	while ( err ) {
		lista_ElemCorriente(lsPares, &aux);
		if (aux.dupla[0] == idAux) {
			getRelacion(&rel, aux.dupla[0], aux.peli, fPer, fPeli);
			if ( lista_Vacia(*ls) )
				lista_Insertar(ls, LSC_PRIMERO, &rel);
			else
				lista_Insertar(ls, LSC_SIGUIENTE, &rel);
			idAux = aux.dupla[1];
		} else {
			getRelacion(&rel, aux.dupla[1], aux.peli, fPer, fPeli);
			if ( lista_Vacia(*ls) )
				lista_Insertar(ls, LSC_PRIMERO, &rel);
			else
				lista_Insertar(ls, LSC_SIGUIENTE, &rel);
			idAux = aux.dupla[0];
		}
		err = lista_MoverCorriente(&lsPares, LSC_SIGUIENTE);
	}

	getRelacion(&rel, idPer2, -1, fPer, fPeli);
	lista_Insertar(ls, LSC_SIGUIENTE, &rel);
}
